/* Copyright (c) 2015,2016 Lucky Byte, Inc.
 */
package com.lucky_byte.pay.jar;

import java.io.File;
import java.io.FileInputStream;
import java.security.KeyStore;
import java.security.KeyStore.Entry;
import java.security.KeyStore.PasswordProtection;
import java.security.KeyStore.PrivateKeyEntry;
import java.security.KeyStore.ProtectionParameter;
import java.security.KeyStore.SecretKeyEntry;
import java.security.KeyStore.TrustedCertificateEntry;
import java.security.KeyStoreException;
import java.security.cert.X509Certificate;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bouncycastle.util.encoders.Base64;
import org.bouncycastle.util.encoders.Hex;

import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.UnrecoverableEntryException;
import java.security.cert.CertificateException;

/**
 * 软件实现的加密机
 */
public class NullEncryptor extends Encryptor
{
	private static final Logger logger = LogManager.getLogger();
	private static final String STORE_PASSWD = "RuntimeUseOnly";
	private static final String PRIMARY_KEYS = "/etc/primary.keys";

	private KeyStore runtime_keystore;
	private File primary_keys_file;
	private File keyset_file;
	private Thread watch_thread;

	/**
	 * 创建软件加密机对象
	 */
	protected NullEncryptor() {
		runtime_keystore = null;
	}

	/**
	 * 监听加密机配置文件修改事件
	 */
	class WatchThread implements Runnable
	{
		private File watch_file;

		public WatchThread(File watch_file) {
			assert(watch_file != null);
			this.watch_file = watch_file;
		}

		@Override
		public void run() {
			try {
				WatchService watchService =
						FileSystems.getDefault().newWatchService();
				Path path = this.watch_file.getParentFile().toPath();
				path.register(watchService, StandardWatchEventKinds.ENTRY_MODIFY);
				while (true) {
					final WatchKey key = watchService.take();
					for (WatchEvent<?> event : key.pollEvents()) {
						Path changed = (Path) event.context();
						if (changed.endsWith(watch_file.getName())) {
							logger.info("检测到加密机配置文件[{}]被修改，重新加载...",
									changed.toString());
							// 清除加密机对象，下次请求时重新初始化
							Encryptor.encryptor = null;
						}
					}
					if (!key.reset()) {
						break;
					}
				}
			} catch (Exception e) {
				logger.error("监听加密机配置文件修改事件错误[{}][{}].",
						e.getMessage(), e.getClass().getSimpleName());
			}
			watch_thread = null;
		}
	}

	/**
	 * 加载 primary.keys
	 */
	private Properties getPrimaryProperties() {
		primary_keys_file = new File(PRIMARY_KEYS);
		if (!primary_keys_file.exists()) {
			logger.error("文件[{}]不存在.", PRIMARY_KEYS);
			return null;
		}
		if (!primary_keys_file.canRead()) {
			logger.error("无权限访问文件[{}]，请联系系统管理员.", PRIMARY_KEYS);
			return null;
		}
		try {
			Properties props = new Properties();
			props.load(new FileInputStream(primary_keys_file));
			return props;
		} catch (IOException e) {
			logger.catching(e);
			logger.error("文件[{}]格式错误.", primary_keys_file.getAbsolutePath());
			return null;
		}
	}

	/**
	 * 读取 keyset.prop
	 */
	private Properties getKeysetProperties() {
		keyset_file = Runtime.fileFor("/sec/keyset.prop");
		if (keyset_file == null || !keyset_file.exists()) {
			logger.error("文件[{}]不存在.", keyset_file.getAbsolutePath());
			return null;
		}
		if (!keyset_file.canRead()) {
			logger.error("无权限访问文件[{}]，请联系系统管理员.",
					keyset_file.getAbsolutePath());
			return null;
		}
		try {
			Properties props = new Properties();
			props.load(new FileInputStream(keyset_file));
			return props;
		} catch (IOException e) {
			logger.catching(e);
			logger.error("文件[{}]格式错误.", keyset_file.getAbsolutePath());
			return null;
		}
	}

	/**
	 * 初始化软件加密机
	 *
	 * @return 成功或失败
	 */
	@Override
	protected synchronized boolean init() {
		// 如果已经初始化，则直接返回
		if (this.runtime_keystore != null) {
			return true;
		}
		Properties primary_prop = this.getPrimaryProperties();
		if (primary_prop == null) {
			logger.error("初始化软件加密机错误.");
			return false;
		}
		Properties keyset_prop = this.getKeysetProperties();
		if (keyset_prop == null) {
			logger.error("初始化软件加密机错误.");
			return false;
		}
		// 创建运行时的 KeyStore 对象
		try {
			runtime_keystore = KeyStore.getInstance("JCEKS");
			runtime_keystore.load(null, STORE_PASSWD.toCharArray());
		} catch (KeyStoreException | IOException |
				NoSuchAlgorithmException | CertificateException e) {
			logger.error("创建密钥存储对象错误[{}].", e);
			return false;
		}
		// 逐个加载 keyset.prop 中配置的信息
		if (!this.loadKeyset(primary_prop, keyset_prop)) {
			runtime_keystore = null;
			return false;
		}
		// 启动配置文件监听线程，如果检查到配置文件发生了变化，则重新初始化加密机
		if (watch_thread == null) {
			watch_thread = new Thread(new WatchThread(this.keyset_file));
			watch_thread.start();
		}
		return true;
	}

	/**
	 * 解析 keyset.prop/primary.keys 中的配置值，格式为:
	 *
	 * @param value 值，格式为 'k1 : v1; k2 : v2; ...'
	 * @return 解析后的 Map，{k1 : v1, k2 : v2}
	 */
	private Map<String, String> parseKeyAttrs(String value) {
		Map<String, String> result = new HashMap<>();
		String[] entries = value.split(";");
		for (String entry : entries) {
			String[] kv = entry.split(":");
			if (kv.length < 2) {
				logger.error("[{}]的格式错误，请检查.", value);
				return null;
			}
			result.put(kv[0].trim(), kv[1].trim());
		}
		return result;
	}

	/**
	 * 逐个加载 keyset.prop 中配置的信息
	 */
	private boolean
	loadKeyset(Properties primary_prop, Properties keyset_prop) {
		for (Object obj : keyset_prop.keySet()) {
			String alias = (String) obj;
			try {
				// 检查名字重复
				if (this.runtime_keystore.containsAlias(alias)) {
					logger.warn("加密机配置文件中包含重复的名字[{}].", alias);
					continue;
				}
			} catch (KeyStoreException e) {
				logger.warn("keystore.containsAlias()错误[{}][{}]",
						e.getMessage(), e.getClass().getSimpleName());
			}
			// 解析 primary.keys 中的配置(如果有的话)
			Map<String, String> primary_attrs;
			String value = primary_prop.getProperty(alias);
			if (value != null) {
				primary_attrs = parseKeyAttrs(value);
				if (primary_attrs == null) {
					logger.error("配置文件[{}]中[{}]格式错误，请查阅文档.",
							PRIMARY_KEYS, value);
					return false;
				}
			} else {
				primary_attrs = new HashMap<>();
			}
			// 解析 keyset.prop 中的配置
			value = keyset_prop.getProperty(alias);
			Map<String, String> key_attrs = parseKeyAttrs(value);
			if (key_attrs == null) {
				logger.error("配置文件[{}]中[{}]格式错误，请查阅文档.",
						keyset_file.getName(), value);
				return false;
			}
			// type 是必须的属性
			String type = key_attrs.get("type");
			if (type == null) {
				logger.error("配置文件[{}]中[{}]缺少[type]属性.",
						keyset_file.getName(), value);
				return false;
			}
			// 根据类型将密钥加载到 KeyStore 中
			boolean retval = true;
			if (type.equalsIgnoreCase("skey")) {
				retval = addSecretKey(primary_attrs, alias, key_attrs);
			} else if (type.equalsIgnoreCase("x509")) {
				retval = addX509Certificate(primary_attrs, alias, key_attrs);
			} else if (type.equalsIgnoreCase("pkcs12")) {
				retval = addPKCS12File(primary_attrs, alias, key_attrs);
			} else if (type.equalsIgnoreCase("jks")) {
				retval = addStoreJKS(primary_attrs, alias, key_attrs);
			} else if (type.equalsIgnoreCase("jceks")) {
				retval = addStoreJCEKS(primary_attrs, alias, key_attrs);
			} else {
				logger.warn("配置文件[{}]中[{}]的类型[{}]不支持，忽略.",
						keyset_file.getName(), value, type);
			}
			if (retval == false) {
				logger.error("添加密钥或证书[{}]失败.", alias);
				return false;
			}
		}
		return true;
	}

	/**
	 * 加入一个对称密钥到 KeyStore 中
	 */
	private boolean addSecretKey(Map<String, String> primary_attrs,
			String alias, Map<String, String> key_attrs) {
		String key = key_attrs.get("key");
		if (key == null) {
			logger.error("类型为[skey]的配置项缺少[key]属性.");
			return false;
		}
		try {
			byte[] compact_key = Hex.decode(key);
			String passwd = primary_attrs.get("passwd");
			if (passwd != null) {
				byte[] decrypt_key =
						Crypto.DESDecrypt(passwd.getBytes(), compact_key, 0);
				compact_key = decrypt_key;
			}
			SecretKey secret_key = new SecretKeySpec(compact_key, "DES");
			SecretKeyEntry entry = new SecretKeyEntry(secret_key);
			ProtectionParameter prot_params =
					new PasswordProtection(STORE_PASSWD.toCharArray());
			runtime_keystore.setEntry(alias, entry, prot_params);
			return true;
		} catch (KeyStoreException e) {
			logger.error("加入安全密钥[{}]错误[{}].", alias, e);
			return false;
		}
	}

	/**
	 * 获取存储的安全密钥
	 *
	 * @return 安全密钥
	 */
	@Override
	public byte[] getSecretKey(String index) {
		if (runtime_keystore == null) {
			logger.error("加密机未初始化，不能获取安全密钥.");
			return null;
		}
		try {
			if (!runtime_keystore.containsAlias(index)) {
				logger.warn("加密机中不包含别名为[{}]的安全密钥.", index);
				return null;
			}
			ProtectionParameter prot_params =
					new PasswordProtection(STORE_PASSWD.toCharArray());
			Entry entry = runtime_keystore.getEntry(index, prot_params);
			if (!(entry instanceof SecretKeyEntry)) {
				logger.error("加密机中与[{}]关联的不是安全密钥，而是[{}].",
						index, entry.getClass().getSimpleName());
				return null;
			}
			return ((SecretKeyEntry) entry).getSecretKey().getEncoded();
		} catch (KeyStoreException | NoSuchAlgorithmException |
				UnrecoverableEntryException e) {
			logger.error("获取安全密钥[{}]错误[{}].", index, e.getMessage());
			return null;
		}
	}

	/**
	 * 添加 PKCS #12 证书
	 */
	@Override
	public boolean addPKCS12Cert(File cert_file, String passwd, String alias) {
		if (!cert_file.exists()) {
			return false;
		}
		if (passwd == null) {
			return false;
		}
		PrivateKeyEntry key_entry =
				Crypto.readPrivateKeyEntry(cert_file, "PKCS12", passwd);
		if (key_entry == null) {
			logger.error("读私钥文件[{}]失败.", cert_file.getAbsolutePath());
			return false;
		}
		try {
			PasswordProtection prot_param =
					new PasswordProtection(STORE_PASSWD.toCharArray());
			runtime_keystore.setEntry(alias, key_entry, prot_param);
			return true;
		} catch (KeyStoreException e) {
			logger.error("添加私钥[{}]失败[{}].", alias, e.getMessage());
			return false;
		}
	}

	@Override
	public boolean addX509Cert(File cert_file, String alias) {
		if (!cert_file.exists()) {
			return false;
		}
		try {
			X509Certificate x509_cert = Crypto.readX509Certificate(cert_file);
			if (x509_cert == null) {
				logger.error("读取 X.509 证书失败，请检查文件格式是否正确.");
				return false;
			}
			Entry entry = new TrustedCertificateEntry(x509_cert);
			if (alias == null) {
				runtime_keystore.setEntry(
						x509_cert.getSerialNumber().toString(),
						entry, null);
			} else {
				runtime_keystore.setEntry(alias, entry, null);
			}
			return true;
		} catch (Exception e) {
			logger.error("添加X.509证书错误[{}].", e.getMessage());
			return false;
		}
	}

	/**
	 * 取成员主密钥，成员主密钥采用固定的索引.
	 */
	public byte[] getMasterKey() {
		byte[] mkey = getSecretKey("master");
		if (mkey == null) {
			logger.fatal("加密机中没有配置机构主密钥.");
			return null;
		}
		if (mkey.length != 16) {
			logger.error("机构主密钥长度[{}]不等于16，无效.", mkey.length);
			return null;
		}
		return mkey;
	}

	/**
	 * 寻找配置中的指定的文件
	 * <p>
	 * 如果文件以 '/' 开头，则认为是绝对路径，否则认为是以 $basedir/sec
	 * 起始的相对路径
	 * </p>
	 */
	private File getSecFile(String path) {
		File file;
		if (path.startsWith("/")) {
			file = new File(path);
		} else {
			file = Runtime.fileFor("/sec/" + path);
		}
		if (file == null || !file.exists()) {
			logger.error("配置中指定的文件[{}]不存在.", path);
			return null;
		}
		if (!file.canRead()) {
			logger.error("[{}]是没有读权限.", path);
			return null;
		}
		return file;
	}

	/**
	 * 添加 X509 公钥证书
	 */
	private boolean addX509Certificate(Map<String, String> primary_attrs,
			String alias, Map<String, String> key_attrs) {
		// file 和 dir 属性都是一个意思，只是适合在不同场景表达
		String path = key_attrs.get("file");
		if (path == null) {
			path = key_attrs.get("dir");
		}
		if (path == null) {
			logger.error("类型为[x509]的配置项缺少[file]或[dir]属性.");
			return false;
		}
		// 在文件系统中定位到文件
		File file_or_dir = getSecFile(path);
		if (file_or_dir == null) {
			return false;
		}
		File[] file_list;
		if (file_or_dir.isDirectory()) {
			file_list = file_or_dir.listFiles();
			alias = null;
		} else {
			file_list = new File[] { file_or_dir };
		}
		// 从文件中读取证书并添加到 keystore 中
		for (File file : file_list) {
			if (!file.isFile()) {
				continue;
			}
			try {
				X509Certificate x509_cert = Crypto.readX509Certificate(file);
				if (x509_cert == null) {
					logger.error("读取 X.509 证书失败，请检查文件格式是否正确.");
					return false;
				}
				Entry entry = new TrustedCertificateEntry(x509_cert);
				if (alias == null) {
					runtime_keystore.setEntry(
							x509_cert.getSerialNumber().toString(),
							entry, null);
				} else {
					runtime_keystore.setEntry(alias, entry, null);
				}
			} catch (Exception e) {
				logger.error("添加X.509证书错误[{}].", e.getMessage());
				return false;
			}
		}
		return true;
	}

	@Override
	public boolean contain(String alias) {
		if (runtime_keystore == null) {
			return false;
		}
		try {
			return runtime_keystore.containsAlias(alias);
		} catch (KeyStoreException e) {
			return false;
		}
	}

	@Override
	public void deleteByAlias(String alias) {
		if (runtime_keystore == null) {
			return;
		}
		try {
			if (runtime_keystore.containsAlias(alias)) {
				runtime_keystore.deleteEntry(alias);
			}
		} catch (KeyStoreException e) {
			logger.error("删除加密机项[{}]错误[{}][{}].", alias,
					e.getMessage(), e.getClass().getSimpleName());
		}
	}

	/**
	 * 从加密机中获取 X.509 公钥证书
	 *
	 * @return 公钥证书
	 */
	@Override
	public X509Certificate getX509Certificate(String alias) {
		if (runtime_keystore == null) {
			logger.error("加密机未初始化，不能获取 X.509 证书.");
			return null;
		}
		try {
			if (!runtime_keystore.containsAlias(alias)) {
				logger.warn("加密机中不包含别名为[{}]的 X.509 证书.", alias);
				return null;
			}
			Entry entry = runtime_keystore.getEntry(alias, null);
			if (!(entry instanceof TrustedCertificateEntry)) {
				logger.error("加密机中与[{}]关联的不是 X.509 证书，而是[{}].",
						alias, entry.getClass().getSimpleName());
				return null;
			}
			return (X509Certificate)
					((TrustedCertificateEntry) entry).getTrustedCertificate();
		} catch (KeyStoreException | NoSuchAlgorithmException |
				UnrecoverableEntryException e) {
			logger.error("获取 X.509 证书[{}]错误[{}].", alias, e.getMessage());
			return null;
		}
	}

	/**
	 * 从文件中导入私钥，这个函数只处理文件中的第一个私钥(主要因为别名原因).
	 */
	private boolean importPrivateKey(Map<String, String> primary_attrs,
			String alias, Map<String, String> key_attrs, String format) {
		String path = key_attrs.get("file");
		if (path == null) {
			logger.error("配置项[{}]缺少[file]属性.", alias);
			return false;
		}
		File key_file = this.getSecFile(path);
		if (key_file == null) {
			return false;
		}
		String passwd = primary_attrs.get("passwd");
		if (passwd == null || passwd.length() == 0) {
			logger.warn("私钥证书[{}]没有设置密码，请检查...", alias);
			return false;
		}
		PrivateKeyEntry key_entry =
				Crypto.readPrivateKeyEntry(key_file, format, passwd);
		if (key_entry == null) {
			logger.error("读私钥文件[{}]失败.", path);
			return false;
		}
		try {
			PasswordProtection prot_param =
					new PasswordProtection(STORE_PASSWD.toCharArray());
			runtime_keystore.setEntry(alias, key_entry, prot_param);
			return true;
		} catch (KeyStoreException e) {
			logger.error("添加私钥[{}]失败[{}].", alias, e.getMessage());
			return false;
		}
	}

	/**
	 * 添加 PKCS #12 文件
	 */
	private boolean addPKCS12File(Map<String, String> primary_attrs,
			String alias, Map<String, String> key_attrs) {
		return importPrivateKey(primary_attrs, alias, key_attrs, "PKCS12");
	}

	/**
	 * 添加 JKS 文件
	 */
	private boolean addStoreJKS(Map<String, String> primary_attrs,
			String alias, Map<String, String> key_attrs) {
		return importPrivateKey(primary_attrs, alias, key_attrs, "JKS");
	}

	/**
	 * 添加 JCEKS 文件
	 */
	private boolean addStoreJCEKS(Map<String, String> primary_attrs,
			String alias, Map<String, String> key_attrs) {
		return importPrivateKey(primary_attrs, alias, key_attrs, "JCEKS");
	}

	/**
	 * 获取私钥记录, 私钥记录包括私钥以及关联的证书
	 *
	 * @return 私钥记录
	 */
	@Override
	public PrivateKeyEntry getPrivateKeyEntry(String alias) {
		if (runtime_keystore == null) {
			logger.error("加密机未初始化，不能获取私钥.");
			return null;
		}
		try {
			if (!runtime_keystore.containsAlias(alias)) {
				logger.warn("加密机中不包含别名为[{}]的私钥.", alias);
				return null;
			}
			ProtectionParameter prot_params =
					new PasswordProtection(STORE_PASSWD.toCharArray());
			Entry entry = runtime_keystore.getEntry(alias, prot_params);
			if (!(entry instanceof PrivateKeyEntry)) {
				logger.error("加密机中与[{}]关联的不是私钥，而是[{}].",
						alias, entry.getClass().getSimpleName());
				return null;
			}
			return (PrivateKeyEntry) entry;
		} catch (KeyStoreException | NoSuchAlgorithmException |
				UnrecoverableEntryException e) {
			logger.error("获取私钥[{}]错误[{}].", alias, e.getMessage());
			return null;
		}
	}

	/**
	 * 通过安全密钥解密其它 MAC/PIN 密钥
	 *
	 * @param key 待解密数据
	 * @return 解密后数据
	 */
	@Override
	public byte[] decrypt(String key_name, byte[] key) {
		byte[] mkey = getSecretKey(key_name);
		if (mkey == null) {
			logger.error("解密失败，未取到主密钥[{}].", key_name);
			return null;
		}
		return Crypto.DESDecryptDED(mkey, key, 0);
	}

	@Override
	public byte[] decrypt(String key_name, String key) {
		return decrypt(key_name, Hex.decode(key));
	}

	/**
	 * 通过安全密钥加密其它 MAC/PIN 密钥
	 *
	 * @param key 待加密密钥
	 * @return 加密后密钥
	 */
	@Override
	public byte[] encrypt(String key_name, byte[] key) {
		byte[] master_key = getSecretKey(key_name);
		if (master_key == null) {
			logger.error("加密失败，未取到主密钥[{}].", key_name);
			return null;
		}
		return Crypto.DESEncryptEDE(master_key, key, 0);
	}

	@Override
	public byte[] encrypt(String key_name, String key) {
		return encrypt(key_name, Hex.decode(key));
	}

	@Override
	public byte[] xor(String key_name, byte[] value) {
		byte[] mkey = getSecretKey(key_name);
		if (mkey == null) {
			logger.error("计算异或值失败，未取到主密钥[{}].", key_name);
			return null;
		}
		if (mkey.length != value.length) {
			logger.error("待计算异或数据长度[{}]与机构主密钥长度[{}]不符.",
					value.length, mkey.length);
			return null;
		}
		byte[] result = new byte[mkey.length];
		for (int i = 0; i < mkey.length; i++) {
			result[i] = (byte) (mkey[i] ^ value[i]);
		}
		return result;
	}

	/**
	 * 基于提供的数据以及密钥计算 MAC
	 *
	 * @param mode MAC 加密模式
	 * @param mackey 加密后的 MAC 密钥
	 * @param macbuf 生成 MAC 的输入数据
	 * @return 8 字节 MAC 字节流，或 null
	 */
	@Override
	public byte[] genMAC(String key_name, int mode,
			String mackey, byte[] macbuf) {
		if (mackey == null || mackey.length() == 0) {
			logger.error("MAC密钥无效导致计算MAC失败.");
			return null;
		}
		return genMAC(key_name, mode, Hex.decode(mackey), macbuf);
	}

	@Override
	public byte[] genMAC(String key_name, int mode,
			byte[] mackey, byte[] macbuf) {
		// 解密 MAC 密钥，MAC 密钥加密后以十六进制 ASCII 字符存储在数据库中
		// 对于 X9.19，规范中要求使用 PIK 作为双倍加密密钥.
		byte[] mackey2 = decrypt(key_name, mackey);
		if (mackey2 == null) {
			logger.error("解密MAC密钥[{}]失败.", mackey);
			return null;
		}
		return genMAC(mode, mackey2, macbuf);
	}

	/**
	 * 生成MAC
	 */
	@Override
	public byte[] genMAC(int mode, byte[] mackey, byte[] macbuf)
	{
		if (mackey == null || macbuf == null) {
			logger.fatal("计算 MAC 的密钥和数据不能为空.");
			return null;
		}
		byte[] mac = null;
		switch (mode) {
		case MAC_MODE_AX9_9:
			mac = genMAC_ANX9_9(mackey, macbuf);
			break;
		case MAC_MODE_AX9_19:
			mac = genMAC_ANX9_19(mackey, macbuf);
			break;
		case MAC_MODE_XOR:
			mac = genMAC_XOR(mackey, macbuf);
			break;
		case MAC_MODE_ECB:
		case MAC_MODE_CUP:
			mac = genMAC_ECB(mackey, macbuf);
			break;
		default:
			logger.error("系统不支持MAC计算方式[{}].", mode);
			return null;
		}
//		logger.trace("MACBUF:\n{}", Utils.formatBytes(macbuf, 0));
//		logger.trace("MAC算法: {}", Encryptor.getMacModeName(mode));
//		logger.trace("MAK: [{}]", Utils.formatBytes(mackey, 0).trim());
//		logger.trace("MAC: [{}]", Utils.formatBytes(mac, 0).trim());
		return mac;
	}

	/**
	 * 按照 ANSI X9.9 模式计算 MAC
	 *
	 * 银联称为单倍长密钥或双倍长密钥算法，如果 mackey 长度为 8，则称为单倍，
	 * 否则称为双倍
	 */
	private byte[] genMAC_ANX9_9(byte[] mackey, byte[] macbuf) {
		// 按 8 字节分组，分配 8 的整倍数空间
		int grp_size = (macbuf.length + 7) / 8 * 8;
		byte[] grp_bytes = new byte[grp_size];
		System.arraycopy(macbuf, 0, grp_bytes, 0, macbuf.length);
		// 按照 8 字节一组，做 XOR + DES 操作，依次循环
		byte[] mac_bytes = new byte[8];
		for (int i = 0; i < grp_size; i += 8) {
			for (int j = 0; j < 8; j++) {
				mac_bytes[j] ^= grp_bytes[i + j];
			}
			if (mackey.length == 16) {
				mac_bytes = ByteUtils.concat(mac_bytes, mac_bytes);
			}
			mac_bytes = Crypto.DESEncryptEDE(mackey, mac_bytes, 0);
			if (mackey.length == 16) {
				mac_bytes = ByteUtils.truncate(mac_bytes, 0, 8);
			}
		}
		return mac_bytes;
	}

	/**
	 * 按照 ANSI X9.19 模式计算 MAC
	 */
	private byte[] genMAC_ANX9_19(byte[] mackey, byte[] macbuf) {
		if (mackey.length != 16) {
			logger.error("X9.19模式需要16字节MAC密钥，当前密钥长度为[{}].",
					mackey.length);
			return null;
		}
		byte[] left_key = new byte[8];
		byte[] right_key = new byte[8];
		System.arraycopy(mackey, 0, left_key, 0, 8);
		System.arraycopy(mackey, 8, right_key, 0, 8);

		byte[] tmp_mac = genMAC_ANX9_9(left_key, macbuf);
		tmp_mac = Crypto.DESDecrypt(right_key, tmp_mac, tmp_mac.length);
		return Crypto.DESEncrypt(left_key, tmp_mac, tmp_mac.length);
	}

	/**
	 * 按照 XOR 模式计算 MAC
	 */
	private byte[] genMAC_XOR(byte[] mackey, byte[] macbuf) {
		// 按 8 字节分组，分配 8 的整倍数空间
		int grp_size = (macbuf.length + 7) / 8 * 8;
		byte[] grp_bytes = new byte[grp_size];
		System.arraycopy(macbuf, 0, grp_bytes, 0, macbuf.length);
		// 按照 8 字节一组，做 XOR 操作，依次循环
		byte[] mac_bytes = new byte[8];
		for (int i = 0; i < grp_size; i += 8) {
			for (int j = 0; j < 8; j++) {
				mac_bytes[j] ^= grp_bytes[i + j];
			}
		}
		return genMAC_ANX9_9(mackey, mac_bytes);
	}

	/**
	 * 按照 ECB 模式计算 MAC
	 */
	private byte[] genMAC_ECB(byte[] mackey, byte[] macbuf) {
		int grp_size = (macbuf.length + 7) / 8 * 8;
		byte[] grp_bytes = new byte[grp_size];
		System.arraycopy(macbuf, 0, grp_bytes, 0, macbuf.length);
		// 按照 8 字节一组，做 XOR 操作，依次循环
		byte[] mac_bytes = new byte[8];
		for (int i = 0; i < grp_size; i += 8) {
			for (int j = 0; j < 8; j++) {
				mac_bytes[j] ^= grp_bytes[i + j];
			}
		}
		// 转换成 16 个 Hex 字节
		byte[] hex_bytes = new String(Hex.encode(mac_bytes))
				.toUpperCase().getBytes();
		// 对前 8 个字节使用 MAK 进行加密
		mac_bytes = Crypto.DESEncryptEDE(mackey, hex_bytes, 8);
		// 加密后结果与后 8 个字节异或
		for (int i = 0; i < 8; i++) {
			mac_bytes[i] ^= hex_bytes[i + 8];
		}
		// 用异或结果再进行一次加密运算
		mac_bytes = Crypto.DESEncryptEDE(mackey, mac_bytes, 8);
		// 转换成 16 个 Hex 字节，返回前 8 字节
		hex_bytes = new String(Hex.encode(mac_bytes))
				.toUpperCase().getBytes();
		return ByteUtils.truncate(hex_bytes, 0, 8);
	}

	/**
	 * 加密二、三磁道数据
	 */
	public byte[] encryptTrack(int track, byte[] tdk, byte[] bytes)
	{
		if (tdk == null || bytes == null) {
			logger.error("加密磁道的输入数据无效.");
			return null;
		}
		switch (track) {
		case TRACK_2:
			return encryptTrack2(tdk, bytes);
		case TRACK_3:
			return encryptTrack2(tdk, bytes);
		default:
			logger.error("请问您是要加密哪个磁道?");
			return null;
		}
	}

	private byte[] encryptTrack2(byte[] tdk, byte[] track_bytes) {
		if (track_bytes.length == 8) {
			boolean allf = true;
			for (int i = 0; i < 8; i++) {
				if ((track_bytes[i] & 0xff) != (0xff & 0xff)) {
					allf = false;
					break;
				}
				if (allf) {
					logger.info("磁道数据为 8 字节全 F，无需加密.");
					return track_bytes;
				}
			}
		}
		if (track_bytes.length < 9) {
			logger.warn("二磁道数据长度[{}]太短，不足以加密.", track_bytes.length);
			return track_bytes;
		}
		int offset = track_bytes.length - 9;
		byte[] clear = new byte[8];
		System.arraycopy(track_bytes, offset, clear, 0, 8);

		byte[] cipher = Crypto.DESEncryptEDE(tdk, clear, 0);
		System.arraycopy(cipher, 0, track_bytes, offset, 8);
		return track_bytes;
	}

	/**
	 * 解密二、三磁道数据
	 */
	@Override
	public byte[] decryptTrack(int track, byte[] tdk, byte[] bytes)
	{
		if (tdk == null || bytes == null) {
			logger.error("解密磁道的输入数据无效.");
			return null;
		}
		switch (track) {
		case TRACK_2:
			return decryptTrack2(tdk, bytes);
		case TRACK_3:
			return decryptTrack2(tdk, bytes);
		default:
			logger.error("请问您是要解密哪个磁道?");
			return null;
		}
	}

	private byte[] decryptTrack2(byte[] tdk, byte[] track_bytes) {
		if (track_bytes.length == 8) {
			boolean allf = true;
			for (int i = 0; i < 8; i++) {
				if ((track_bytes[i] & 0xff) != (0xff & 0xff)) {
					allf = false;
					break;
				}
				if (allf) {
					logger.info("磁道数据为 8 字节全 F，无需解密.");
					return track_bytes;
				}
			}
		}
		if (track_bytes.length < 9) {
			logger.warn("二磁道数据长度[{}]太短，不足以解密.", track_bytes.length);
			return track_bytes;
		}
		int offset = track_bytes.length - 9;
		byte[] cipher = new byte[8];
		System.arraycopy(track_bytes, offset, cipher, 0, 8);
		byte[] clear = Crypto.DESDecryptDED(tdk, cipher, 0);
		System.arraycopy(clear, 0, track_bytes, offset, 8);
		return track_bytes;
	}

	/**
	 * 加密 PIN
	 */
	public byte[] encryptPin(byte[] pik, byte[] pin, byte[] pan) {
		if (pik == null || pin == null) {
			logger.warn("加密PIN的输入信息无效.");
			return null;
		}
		if (pin.length != 8) {
			logger.warn("加密PIN失败，PIN的长度[{}]不等于 8.", pin.length);
			return null;
		}
		byte[] xor = pin.clone();
		if (pan != null) {
			for (int i = 0; i < 8; i++) {
				xor[i] ^= pan[i];
			}
		}
		byte[] pin_enc = Crypto.DESEncryptEDE(pik, xor, 0);

//		logger.trace("PIK: [{}]", Utils.formatBytes(pik, 0).trim());
//		logger.trace("PIN: [{}]", Utils.formatBytes(pin, 0).trim());
//		logger.trace("PAN: [{}]", Utils.formatBytes(pan, 0).trim());
//		logger.trace("加密后PIN：[{}]", Utils.formatBytes(pin_enc, 0).trim());
		return pin_enc;
	}

	/**
	 * 解密 PIN
	 */
	@Override
	public byte[] decryptPin(byte[] pik, byte[] pin, byte[] pan) {
		if (pik == null || pin == null) {
			logger.warn("解密PIN的输入信息无效.");
			return null;
		}
		if (pin.length != 8) {
			logger.warn("解密PIN失败，PIN的长度[{}]不等于8.", pin.length);
			return null;
		}
		byte[] xor = Crypto.DESDecryptDED(pik, pin, 0);
		if (pan != null) {
			for (int i = 0; i < 8; i++) {
				xor[i] ^= pan[i];
			}
		}
//		logger.trace("PIK: [{}]", Utils.formatBytes(pik, 0).trim());
//		logger.trace("PIN: [{}]", Utils.formatBytes(pin, 0).trim());
//		logger.trace("PAN: [{}]", Utils.formatBytes(pan, 0).trim());
//		logger.trace("解密后PIN：[{}]", Utils.formatBytes(xor, 0).trim());
		return xor;
	}

	/**
	 * 计算 SHA1 摘要
	 *
	 * @return 摘要
	 */
	@Override
	public byte[] sha1(byte[] input, int offset, int length) {
		return Crypto.sha1(input, offset, length);
	}

	/**
	 * 计算 SHA1 摘要
	 *
	 * @param input 输入数据
	 * @return 摘要
	 */
	@Override
	public byte[] sha1(byte[] input) {
		return sha1(input, 0, 0);
	}

	/**
	 * 计算 SHA1withRSA 数字签名
	 *
	 * @param key_name 证书别名
	 * @param input 输入数据
	 * @param offset 输入数据偏移
	 * @param length 输入数据长度
	 * @return 签名结果
	 */
	@Override
	public byte[] signSHA1WithRSA(String key_name, byte[] input, int offset, int length) {
		PrivateKeyEntry entry = getPrivateKeyEntry(key_name);
		if (entry == null) {
			logger.error("未找到[{}]对应的私钥，签名失败.");
			return null;
		}
		logger.info("使用证书[{}]对报文进行签名.", key_name);
		return Crypto.signSHA1WithRSA(entry.getPrivateKey(), input, offset, length);
	}

	/**
	 * 计算 SHA1withRSA 数字签名
	 *
	 * @param key_name 证书别名
	 * @param input 输入数据
	 * @return 签名结果
	 */
	@Override
	public byte[] signSHA1WithRSA(String key_name, byte[] input) {
		return signSHA1WithRSA(key_name, input, 0, 0);
	}

	/**
	 * 验证 SHA1withRSA 数字签名
	 *
	 * @return 是否验证通过
	 */
	@Override
	public boolean verifySignSHA1WithRSA(String cert_serial,
			byte[] input, byte[] to_verify) {
		try {
			Enumeration<?> enumer = runtime_keystore.aliases();
			if (!enumer.hasMoreElements()) {
				logger.error("加密机中无任何记录，请检查.");
				return false;
			}
			while (enumer.hasMoreElements()) {
				String alias = (String) enumer.nextElement();
				if (!runtime_keystore.isCertificateEntry(alias)) {
					continue;
				}
				X509Certificate cert = (X509Certificate)
						runtime_keystore.getCertificate(alias);
				if (cert.getSerialNumber().toString().equals(cert_serial)) {
					return Crypto.verifySHA1WithRSA(cert, input, to_verify);
				}
			}
			logger.error("加密机中未找到序号为[{}]的验签证书.", cert_serial);
			return false;
		} catch (Exception e) {
			logger.error("验证签名错误[{}].", e.getMessage());
			return false;
		}
	}

	public boolean verifySignSHA1WithRSAByAlias(String key_name,
			byte[] input, byte[] to_verify) {
		X509Certificate cert = this.getX509Certificate(key_name);
		if (cert == null) {
			logger.error("未找到名称为[{}]的公钥证书.", key_name);
			return false;
		}
		return Crypto.verifySHA1WithRSA(cert, input, to_verify);
	}

	/**
	 * 计算 MD5withRSA 数字签名
	 *
	 * @param key_name 证书别名
	 * @param input 输入数据
	 * @param offset 输入数据偏移
	 * @param length 输入数据长度
	 * @return 签名结果
	 */
	@Override
	public byte[] signMD5WithRSA(String key_name, byte[] input, int offset, int length) {
		PrivateKeyEntry entry = getPrivateKeyEntry(key_name);
		if (entry == null) {
			logger.error("未找到[{}]对应的私钥，签名失败.");
			return null;
		}
		logger.info("使用证书[{}]对报文进行签名.", key_name);
		return Crypto.signMD5WithRSA(entry.getPrivateKey(), input, offset, length);
	}

	/**
	 * 计算 MD5withRSA 数字签名
	 *
	 * @param key_name 证书别名
	 * @param input 输入数据
	 * @return 签名结果
	 */
	@Override
	public byte[] signMD5WithRSA(String key_name, byte[] input) {
		return signMD5WithRSA(key_name, input, 0, 0);
	}


	/**
	 * 验证 MD5withRSA 数字签名
	 *
	 * @return 是否验证通过
	 */
	@Override
	public boolean verifySignMD5WithRSA(String cert_serial,
			byte[] input, byte[] to_verify) {
		try {
			Enumeration<?> enumer = runtime_keystore.aliases();
			if (!enumer.hasMoreElements()) {
				logger.error("加密机中无任何记录，请检查.");
				return false;
			}
			while (enumer.hasMoreElements()) {
				String alias = (String) enumer.nextElement();
				if (!runtime_keystore.isCertificateEntry(alias)) {
					continue;
				}
				X509Certificate cert = (X509Certificate)
						runtime_keystore.getCertificate(alias);
				if (cert.getSerialNumber().toString().equals(cert_serial)) {
					return Crypto.verifyMD5WithRSA(cert, input, to_verify);
				}
			}
			logger.error("加密机中未找到序号为[{}]的验签证书.", cert_serial);
			return false;
		} catch (Exception e) {
			logger.error("验证签名错误[{}].", e.getMessage());
			return false;
		}
	}

	public boolean verifySignMD5WithRSAByAlias(String key_name,
			byte[] input, byte[] to_verify) {
		X509Certificate cert = this.getX509Certificate(key_name);
		if (cert == null) {
			logger.error("未找到名称为[{}]的公钥证书.", key_name);
			return false;
		}
		return Crypto.verifyMD5WithRSA(cert, input, to_verify);
	}

	/**
	 * 银联全渠道公钥加密
	 *
	 * @param key_name: 公钥证书名
	 *
	 * @return 加密数据
	 */
	@Override
	public String AcpEncrypt(String key_name, byte[] data_clear) {
		X509Certificate cert = this.getX509Certificate(key_name);
		if (cert == null) {
			logger.error("加密机中未找到加密证书[{}]，加密失败.", key_name);
			return null;
		}
		PublicKey public_key = cert.getPublicKey();
		if (public_key == null) {
			logger.error("取证书公钥失败，加密失败.");
			return null;
		}
		try {
			Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", "BC");
			cipher.init(Cipher.ENCRYPT_MODE, public_key);
			int block_size = cipher.getBlockSize();
			int outsize = cipher.getOutputSize(data_clear.length);
			int blocks = (data_clear.length + block_size - 1) / block_size;
			byte[] result = new byte[blocks * outsize];
			for (int i = 0; i < blocks; i++) {
				if (i < blocks - 1) {
					cipher.doFinal(data_clear, i * block_size, block_size,
							result, i * outsize);
				} else {
					cipher.doFinal(data_clear, i * block_size,
							data_clear.length % block_size, result, i * outsize);
				}
			}
			return new String(Base64.encode(result));
		} catch (Exception e) {
			logger.error("公钥加密错误[{}][{}].", e.getMessage(),
					e.getClass().getSimpleName());
			return null;
		}
	}

	/**
	 * 银联全渠道公钥解密
	 *
	 * @param key_name: 私钥证书名
	 *
	 * @return 解密数据
	 */
	public byte[] AcpDecrypt(String key_name, byte[] data_cipher) {
		PrivateKeyEntry entry = this.getPrivateKeyEntry(key_name);
		if (entry == null) {
			logger.error("加密机中未找到解密私钥证书[{}]，解密失败.", key_name);
			return null;
		}
		PrivateKey private_key = entry.getPrivateKey();
		if (private_key == null) {
			logger.error("取证书私钥失败，解密失败.");
			return null;
		}
		try {
			Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", "BC");
			cipher.init(Cipher.DECRYPT_MODE, private_key);
			int block_size = cipher.getBlockSize();
			int outsize = cipher.getOutputSize(data_cipher.length);
			int blocks = (data_cipher.length + block_size - 1) / block_size;
			byte[] result = new byte[blocks * outsize];
			for (int i = 0; i < blocks; i++) {
				if (i < blocks - 1) {
					cipher.doFinal(data_cipher, i * block_size, block_size,
							result, i * outsize);
				} else {
					cipher.doFinal(data_cipher, i * block_size,
							data_cipher.length % block_size, result, i * outsize);
				}
			}
			return result;
		} catch (Exception e) {
			logger.error("使用私钥解密错误[{}][{}].", e.getMessage(),
					e.getClass().getSimpleName());
			return null;
		}
	}

	/**
	 * 用于加密全渠道敏感账户信息包括PIN、磁道信息(含芯片等效磁道信息)、CVN2和卡片有效期等等
	 *
	 * @param key_name: 公钥证书名
	 *
	 * @return 加密数据
	 */
	public byte[] AcpEncryptPin(String key_name, byte[] pin_clear) {
		return null;
	}

	/**
	 * 用于解密全渠道敏感账户信息包括PIN、磁道信息(含芯片等效磁道信息)、CVN2和卡片有效期等等
	 *
	 * @param key_name: 私钥证书名
	 *
	 * @return 解密数据
	 */
	public byte[] AcpPKDeccryptPin(String key_name, byte[] pin_cipher) {
		return null;
	}
}
